
#include <BMap>
#include <BRender>
#include <BOpenGL>

#include <BPolyStripe>
#include "member_BPolyStripe.h"

using namespace BWE;

#define member					(*(member_BPolyStripe*)_ptr)
#define member_allocate()		_ptr = new member_BPolyStripe(this)
#define member_release()		delete (member_BPolyStripe*)_ptr

BPolyStripe::BPolyStripe()
{
	member_allocate();
}
BPolyStripe::BPolyStripe(const BVectorArray& linePoints)
{
	member_allocate();
	member.linePoints = linePoints;
	member.dirty = true;
}
BPolyStripe::~BPolyStripe()
{
	member_release();
}

void BPolyStripe::copy(const BShape* other)
{
	BShape::copy(other);
	if (const BPolyStripe* stripe = dynamic_cast<const BPolyStripe*>(other))
	{
		member.thickness = stripe_member(stripe)->thickness;
		member.linePoints = stripe_member(stripe)->linePoints;
		member.length = stripe_member(stripe)->length;
		member.dirty = stripe_member(stripe)->dirty;
	}
}
BShape* BPolyStripe::clone() const
{
	BPolyStripe* stripe = new BPolyStripe();
	stripe->copy(this);
	return stripe;
}

void BPolyStripe::setCloseLoop(bool closeLoop)
{
	if (member.closeLoop != closeLoop)
	{
		member.closeLoop = closeLoop;
		this->dirty();
	}
}
bool BPolyStripe::closeLoop() const
{
	return member.closeLoop;
}

void BPolyStripe::setLinePoints(const BVectorArray& linePoints)
{
	member.linePoints = linePoints;
	member.dirty = true;
	this->dirty();
}
const BVectorArray& BPolyStripe::linePoints() const
{
	return member.linePoints;
}

bool BPolyStripe::appendPoint(const BVector& point)
{
	if (member.linePoints.append(point))
	{
		this->dirty();
		return true;
	}
	return false;
}
bool BPolyStripe::removePoint(int index)
{
	if (member.linePoints.remove(index))
	{
		this->dirty();
		return true;
	}
	return false;
}

int BPolyStripe::pointCount() const
{
	return member.linePoints.size();
}

bool BPolyStripe::setPoint(int index, const BVector& point)
{
	return member.linePoints.set(index, point);
}
const BVector& BPolyStripe::point(int index) const
{
	return member.linePoints.at(index);
}

BReal BPolyStripe::length() const
{
	this->flush();
	return member.length;
}
bool BPolyStripe::reverse()
{
	if (member.linePoints.size())
	{
		member.linePoints.reverse();
		this->dirty();
		return true;
	}
	return false;
}

void BPolyStripe::setThickness(BReal thickness)
{
	if (member.thickness != thickness)
	{
		member.thickness = thickness;
		this->dirty();
	}
}
BReal BPolyStripe::thickness() const
{
	return member.thickness;
}

void BPolyStripe::setPolygon(const BPolygon& polygon)
{
	member.polygon = polygon;
	member.dirty = true;
	this->dirty();
}
const BPolygon& BPolyStripe::polygon() const
{
	return member.polygon;
}

bool BPolyStripe::query(const BString& text, BValue& value) const
{
	return BShape::query(text, value);
}

void BPolyStripe::slotTransformed(const BObject* object, const BValue& value)
{

}

void BPolyStripe::refresh(BBox& box)
{
	member.length = 0;
	for (int i = 1; i < member.linePoints.size(); i++)
	{
		const BVector& v0 = member.linePoints[i - 1];
		const BVector& v1 = member.linePoints.at(i);
		member.length += (v0 - v1).length();
	}
	if (member.polygon.size())
	{
		BPolygon polygon = member.polygon - member.linePoints.first();
		BVectorArray* vertices = (BVectorArray*)this->vertices();
		if (!vertices)
			vertices = new BVectorArray();
		BVectorArray* normals = (BVectorArray*)this->normals();
		if (!normals)
			normals = new BVectorArray();
		vertices->resize(polygon.size() * member.linePoints.size());
		normals->resize(polygon.size() * member.linePoints.size());
		BLine line(member.linePoints[0], member.linePoints[1]);
		vertices->fill(member.polygon.vertices());
		for (int i = 1; i < member.linePoints.size(); i++)
		{
			BVector* vs = vertices->data(i * polygon.size());
			BVector* ns = normals->data(i * polygon.size());
			const BVector& v0 = member.linePoints[i - 1];
			const BVector& v1 = member.linePoints.at(i);
			BVector direct = (v1 - v0);
			direct.normalize();
			BQuater quater(line.direct(), direct);
			for (int j = 0; j < polygon.size(); j++)
			{
				vs[j] = polygon.at(j);
				vs[j] *= quater;
				vs[j] += v1;
			}
		}
		((BPolyStripe*)this)->clearElements();
		BIntArray* indices = new BIntArray();
		for (int r = 0; r < member.linePoints.size() - 1; r++)
		{
			int head = r * polygon.size();
			for (int c = 0; c < polygon.size() - 1; c++)
			{
				int i0 = head + c;
				int i1 = i0 + polygon.size();
				int i2 = i1 + 1;
				int i3 = i0 + 1;
				indices->append(i0);
				indices->append(i1);
				indices->append(i2);
				indices->append(i3);
			}
			int i0 = head;
			int i1 = i0 + polygon.size() - 1;
			int i2 = i1 + polygon.size();
			int i3 = i0 + polygon.size();
			indices->append(i0);
			indices->append(i1);
			indices->append(i2);
			indices->append(i3);
		}
		if (member.closeLoop)
		{
			int last = (member.linePoints.size() - 1) * polygon.size();
			for (int c = 0; c < polygon.size() - 1; c++)
			{
				int i0 = last + c;
				int i1 = c;
				int i2 = i1 + 1;
				int i3 = i0 + 1;
				indices->append(i0);
				indices->append(i1);
				indices->append(i2);
				indices->append(i3);
			}
			int i0 = last;
			int i1 = last + polygon.size() - 1;
			int i2 = polygon.size() - 1;
			int i3 = 0;
			indices->append(i0);
			indices->append(i1);
			indices->append(i2);
			indices->append(i3);
		}
		((BPolyStripe*)this)->addElement(Primitive_Quads, indices);
		((BPolyStripe*)this)->smooth();
	}
	BGeometry::refresh(box);
}
void BPolyStripe::render(BRender& render)
{
	BGeometry::render(render);
}